--[[
	Copyright (C) Udorn (Blackhand)

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
--]]

--[[
	Generic table for managing items. May be used to show auctions for example.
--]]
vendor.ItemTable = {}
vendor.ItemTable.prototype = {}
vendor.ItemTable.metatable = {__index = vendor.ItemTable.prototype}

local L = vendor.Locale.GetInstance()

local created = 0
local SCROLLER_WIDTH = 27
local SELECT_COL = 76783
local CMD_HEIGHT = 20
local CMD_WIDTH = 90
local FRAME_BACKGROUND = {r = 0, g = 0, b = 0, a = 1}
local CMDS_BACKGROUND = {r = 0.1, g = 0.1, b = 0.1, a = 0.8}
local TOP_INSET = 3
local RIGHT_INSET = 2

--[[
	Updates the arrow indicator of the given button.
--]]
local function _UpdateArrow(self, bt, sortId, ascending)
	local arrow = getglobal(bt:GetName().."Arrow");
	if (bt.sortId == sortId) then
		arrow:Show();
		if (not ascending) then
			arrow:SetTexCoord(0, 0.5625, 1.0, 0);
		else
			arrow:SetTexCoord(0, 0.5625, 0, 1.0);
		end
	else
		arrow:Hide();
	end
end

--[[
	Updates the state of the sort buttons.
--]]
local function _UpdateSortButtons(self)
	local sortId, ascending = self.model:GetSortInfo()
	for _,col in pairs(self.cols) do
		if (col.sortButton) then
			_UpdateArrow(self, col.sortButton, sortId, ascending)
		end
	end
end

--[[
	Callback for updating the scroll frame.
--]]
function _ScrollUpdate(frame)
	local self = frame.obj
	_UpdateSortButtons(self)
	local n = self.model:Size()
	local offset = FauxScrollFrame_GetOffset(self.scrollFrame)
	local isScrolling = n > self.cfg.numVisibleItems
	-- set sort buttons to correct size
	for _,col in pairs(self.cols) do
		if (col.sortButton) then
			if (isScrolling) then
				if (col.isLastCol) then
					col.sortButton:SetWidth(col.minWidth + RIGHT_INSET)
				else
					col.sortButton:SetWidth(col.minWidth)
				end
			else
				if (col.isLastCol) then
					col.sortButton:SetWidth(col.maxWidth + RIGHT_INSET)
			 	else
					col.sortButton:SetWidth(col.maxWidth)
				end
			end
		end
	end
	for i=1,self.cfg.numVisibleItems do
		local index = offset + i
		for _,col in pairs(self.cols) do
			local cell = self.cells[i.."-"..col.id]
			if (index > n) then
				cell:Hide()
			else
				if (isScrolling) then
					cell:SetWidth(col.minWidth)
				else
					cell:SetWidth(col.maxWidth)
				end
				cell:Show()
				if (col.type == "check") then
					cell:SetChecked(self.model:IsSelected(index))
        		elseif (col.type == "texture") then
        			local texture = self.model:GetValues(index, col.id)
        			cell:SetNormalTexture(texture)
        		elseif (col.type == "string") then
        			local str = self.model:GetValues(index, col.id)
        			cell:SetText(str)
        		elseif (col.type == "number") then
        			local num = self.model:GetValues(index, col.id)
        			cell:SetText(num)
        		else
        			error("unknown column type: "..(col.type or "NA"))
        		end
			end
		end
	end
	FauxScrollFrame_Update(self.scrollFrame, n, self.cfg.numVisibleItems, self.rowHeight)
end

--[[
	Toggles the sort state for the given type. If it's currently not
	the sorting criteria, then it will be activated and set to ascending.
--]]
local function _ToggleSort(self, bt, id)
	self.model:ToggleSort(id)
	_UpdateSortButtons(self)
	_ScrollUpdate(self.scrollFrame)
end

--[[
	Calculates the columns for the specified model.
--]] 
local function _CreateColumns(self)
	self.rowHeight = (self.height - CMD_HEIGHT - TOP_INSET) / self.cfg.numVisibleItems
	self.cols = wipe(self.cols or {})
	local width = 0
	local weight = 0
	-- first column is the selection checkmark
	local col = {}
	col.type = "check"
	col.id = SELECT_COL
	col.minWidth = self.rowHeight
	col.maxWidth = self.rowHeight
	width = width + col.minWidth
	table.insert(self.cols, col)
	-- calculate the base widths
	for _,id in pairs(self.cfg.selected) do
		vendor.Vendor:Debug("handle selected id: %d", id)
		local col = {}
		local desc = self.model:GetColumnDescriptor(id)
		col.type = desc.type
		col.title = desc.title
		col.id = id
		col.sortable = desc.sortable
		col.align = desc.align
		if (col.type == "texture") then
			col.minWidth = self.rowHeight
		elseif (col.type == "string") then
			col.minWidth = desc.minWidth
			col.weight = desc.weight
		elseif (col.type == "number") then
			col.minWidth = desc.minWidth
			col.weight = desc.weight
		else
			error("unknown column type: "..(col.type or "NA"))
		end
		col.maxWidth = col.minWidth
		width = width + col.minWidth
		weight = weight + (col.weight or 0)
		table.insert(self.cols, col)
	end
	-- calculate final widths according to weights
	local minWidth = self.width - SCROLLER_WIDTH - RIGHT_INSET
	local maxWidth = self.width - RIGHT_INSET
	if (width > minWidth) then
		error("too large width: "..width)
	end
	local diff = minWidth - width
	local maxDiff = maxWidth - width
	local prev = nil
	for _,col in pairs(self.cols) do
		if (col.weight and col.weight > 0) then
			local fraction = col.weight / weight
			local minWidth = col.minWidth
			col.minWidth = minWidth + (diff * fraction)
			col.maxWidth = minWidth + (maxDiff * fraction)
		end
		if (prev) then
			prev.isLastCol = nil
		end
		col.isLastCol = true
		prev = col
	end
end

local function _SelectedItem(self, idx, isSelected)
	local offset = FauxScrollFrame_GetOffset(self.scrollFrame)
	local row = offset + idx
	self.model:SelectItem(row, isSelected)
end

--[[
	Creates the i'th row.
--]]
local function _CreateRow(self, parent, i)
	local off = 0
	local prev = nil
	for _,col in pairs(self.cols) do
		if (col.type == "check") then
			local but = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
			but.obj = self 
			but.idx = i
			but:SetWidth(col.minWidth)
			but:SetHeight(self.rowHeight)
			if (prev) then
				but:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
			else
				but:SetPoint("TOPLEFT", off, -((i - 1) * self.rowHeight + TOP_INSET))
			end
			but:SetScript("OnClick", function(but) _SelectedItem(but.obj, but.idx, but:GetChecked()) end)
			self.cells[i.."-"..col.id] = but
			prev = but
		elseif (col.type == "texture") then
			local but = CreateFrame("Button", nil, parent)
			but.obj = self 
			but:SetWidth(col.minWidth)
			but:SetHeight(self.rowHeight)
			if (prev) then
				but:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
			else
				but:SetPoint("TOPLEFT", off, -((i - 1) * self.rowHeight + TOP_INSET))
			end
			self.cells[i.."-"..col.id] = but
			prev = but
		elseif (col.type == "string") then
			local f = parent:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
			f:SetWidth(col.minWidth)
			f:SetHeight(self.rowHeight)
			if (prev) then
				f:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
			else
				f:SetPoint("TOPLEFT", off, -((i - 1) * self.rowHeight + TOP_INSET))
			end
			if (col.align) then
				f:SetJustifyH(col.align)
			end
			self.cells[i.."-"..col.id] = f
			prev = f
		elseif (col.type == "number") then
			local f = parent:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
			f:SetWidth(col.minWidth)
			f:SetHeight(self.rowHeight)
			if (prev) then
				f:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
			else
				f:SetPoint("TOPLEFT", off, -((i - 1) * self.rowHeight + TOP_INSET))
			end
			if (col.align) then
				f:SetJustifyH(col.align)
			end
			self.cells[i.."-"..col.id] = f
			prev = f
		else
			error("unknown column type: "..(col.type or "NA"))
		end
		off = off + col.minWidth
	end
end

--[[
	Creates the sort buttons.
--]]
local function _CreateSortButtons(self, parent)
	local prev = nil
	local off = 0
	for idx,col in pairs(self.cols) do
		local name = "AMItmTblSrtBt"..created.."-"..idx
		local but = CreateFrame("Button", name, parent, "AuctionSortButtonTemplate")
		but:SetText(col.title)
		if (prev) then
			but:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
		else
			but:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 19)
		end
		but:SetWidth(col.minWidth)
		but:SetHeight(19)
		but.sortId = col.id
		but:SetScript("OnClick", function(but) _ToggleSort(but.obj, but, but.sortId) end)
		but.obj = self
		col.sortButton = but
		prev = but
		if (not col.title or not col.sortable) then
			-- it has to be created for correct sizing
			but:Hide()
		end
	end
end

--[[
	Creates the list of cmds in the footer.
--]]
local function _CreateCmds(self)
	local frame = CreateFrame("Frame", nil, self.frame)
	frame:SetWidth(self.width)
	frame:SetHeight(CMD_HEIGHT)
	frame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", 0, 0)
	-- create a solid background
	local texture = frame:CreateTexture()
	texture:SetAllPoints(frame)
	vendor.GuiTools.SetColor(texture, CMDS_BACKGROUND)
	-- calc (right) width
	local width = 0
	for k,cmd in pairs(self.cmds) do
		cmd.width = cmd.width or CMD_WIDTH
		if (not cmd.alignLeft) then
			width = width + cmd.width
		end
	end
	-- create buttons
	for i=1,2 do
		local off = 0
		local diffMul = 1
		if (i == 2) then
			off = width
			diffMul = -1
		end
    	for k,cmd in pairs(self.cmds) do
    		if ((i == 1 and cmd.alignLeft) or (i == 2 and not cmd.alignLeft)) then
        		local but = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
        		but:SetText(cmd.title)
        		but:SetWidth(cmd.width)
        		but:SetHeight(CMD_HEIGHT)
        		if (cmd.alignLeft) then
        			but:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", off, 0)
        		else
        			but:SetPoint("BOTTOMLEFT", frame, "BOTTOMRIGHT", -off, 0)
        		end
        		but.func = cmd.func
        		but.arg1 = cmd.arg1
        		but.enabledFunc = cmd.enabledFunc
        		but:SetScript("OnClick", function(but) but.func(but.arg1) end)
        		but:SetScript("OnUpdate", function(but)
        				if (but.enabledFunc) then
        					if (but.enabledFunc(but.arg1)) then
        						but:Enable()
        					else
        						but:Disable()
        					end
        				end
        			end)
        		off = off + diffMul * cmd.width
        	end
    	end
    end
end

--[[
	Creates the frame of the table, according to the model and config.
--]]
local function _CreateFrame(self)
	local frame = CreateFrame("Frame", nil, self.parent)
	frame:SetWidth(self.width)
	frame:SetHeight(self.height)
	-- adds a solid background
	local texture = frame:CreateTexture()
	texture:SetAllPoints(frame)
	vendor.GuiTools.SetColor(texture, FRAME_BACKGROUND)

	-- scrollframe
	created = created + 1
	local scroll = CreateFrame("ScrollFrame", "AuctionMasterItemTableScroll"..created, frame, "FauxScrollFrameTemplate")
	scroll.obj = self
	scroll:SetWidth(self.width - SCROLLER_WIDTH)
	scroll:SetHeight(self.height - CMD_HEIGHT)
	scroll:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -SCROLLER_WIDTH, 0)
	scroll:SetScript("OnVerticalScroll", function(but, value) FauxScrollFrame_OnVerticalScroll(but, value, but.obj.rowHeight, _ScrollUpdate) end)
	scroll:SetScript("OnShow", function(but) _ScrollUpdate(but) end)
	local t = scroll:CreateTexture(nil, "ARTWORK")
	t:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
	t:SetWidth(31)
	t:SetHeight(256)
	t:SetPoint("TOPLEFT", scroll, "TOPRIGHT", -2, 5)
	t:SetTexCoord(0, 0.484375, 0, 1.0)

	t = scroll:CreateTexture(nil, "ARTWORK")
	t:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
	t:SetWidth(31)
	t:SetHeight(106)
	t:SetPoint("BOTTOMLEFT", scroll, "BOTTOMRIGHT", -2, -2)
	t:SetTexCoord(0.515625, 1.0, 0, 0.4140625)

	_CreateSortButtons(self, frame)
	
	-- visible rows
	self.cells = {}
	for i=1,self.cfg.numVisibleItems do
		_CreateRow(self, frame, i)
	end
	
	self.frame = frame
	self.scrollFrame = scroll
end

--[[
	Creates a new instance.
--]]
function vendor.ItemTable:new(parent, itemModel, cmds, itemTableConfig, width, height)
	local instance = setmetatable({}, self.metatable)
	instance.parent = parent
	instance.model = itemModel
	instance.cfg = itemTableConfig
	instance.width = width
	instance.height = height
	instance.cmds = cmds
	_CreateColumns(instance)
	_CreateFrame(instance)
	_CreateCmds(instance)
	return instance
end

--[[
	Creates a default config for an ItemTable. The arguments are the column ids to be selected.
--]]
function vendor.ItemTable.CreateConfig(...)
	local config = {}
	config.numVisibleItems = 15
	config.selected = {}
	for i=1,select('#', ...) do
		local id = select(i, ...)
		vendor.Vendor:Debug("add id %d", id)
		table.insert(config.selected, id) 
	end
	return config
end

--[[
	Sets the position of the frame.
--]]
function vendor.ItemTable.prototype:SetPoint(...)
	self.frame:SetPoint(...)
end

--[[
	Shows the frame.
--]]
function vendor.ItemTable.prototype:Show()
	self.frame:Show()
	_ScrollUpdate(self.scrollFrame)
end

--[[
	Hides the frame.
--]]
function vendor.ItemTable.prototype:Hide()
	self.frame:Hide()
end
